package crond

import (
	"context"
	"encoding/base64"
	"os"
	"strings"
	"time"

	"device-admin/config/mysql"
	"device-admin/internal/app/grpc/protoc/devicec"
	"device-admin/internal/app/grpc/protoc/robotc"
	"device-admin/internal/app/grpc/service/pubsub"
	"device-admin/internal/app/grpc/service/terminal"
	"device-admin/internal/dal"
	"device-admin/internal/dao"
	"device-admin/internal/define"
	"device-admin/internal/models"

	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"google.golang.org/protobuf/proto"
)

const MaxUpgradeNum = 4

func TerminalUpgrade() {
	db := mysql.NewDB()
	var scheduleIDs []string     // 正常模式的计划
	var testScheduleIDs []string // 压测模式的计划
	db.Model(&models.UpgradeSchedule{}).
		Where("start_time <= ?", time.Now().Unix()).
		Where("end_time >= ?", time.Now().Unix()).
		Where("mode = 1").
		Pluck("id", &scheduleIDs)

	db.Model(&models.UpgradeSchedule{}).
		Where("start_time <= ?", time.Now().Unix()).
		Where("end_time >= ?", time.Now().Unix()).
		Where("mode = 2").
		Pluck("id", &testScheduleIDs)
	// 调度超时/下载响应超时的重新调度
	{
		db.Model(&models.UpgradeTerminals{}).
			// 状态过滤
			Where(
				// 正常模式，会调度等待下载和下载中的
				db.Where(
					db.Where("schedule_id IN ?", scheduleIDs).
						Where("upgrade_status = 1 OR upgrade_status = 3"),
				).
					// 压测模式，会调度等待下载，下载中和升级成功的
					Or(
						db.Where("schedule_id IN ?", testScheduleIDs).
							Where(
								db.Where("upgrade_status = 1 OR upgrade_status = 3 OR upgrade_status = 5"),
							),
					),
			).
			Where("updated_at < ?", time.Now().Unix()-180).
			Updates(map[string]interface{}{
				"upgrade_status": 0,
			})
	}
	// 升级调度
	{
		// 当前正等待下载或下载中的设备，且处于在线状态
		var count int64
		db.Model(&models.UpgradeTerminals{}).
			Joins("JOIN terminal ON terminal.robot_id = upgrade_terminals.robot_id AND terminal.terminal_type = upgrade_terminals.terminal_type").
			// 不管是正式模式还是压测模式
			Where(
				db.Where("schedule_id IN ?", scheduleIDs).Or("schedule_id IN ?", testScheduleIDs),
			).
			Where("upgrade_terminals.upgrade_status = 1 OR upgrade_terminals.upgrade_status = 3").
			Where("terminal.latest_active_time > ?", time.Now().Unix()-60).
			Count(&count)
		// 当小于最大允许数量时，进行调度
		if count < MaxUpgradeNum {
			var upgradeTerminals []models.UpgradeTerminals
			db.Model(&models.UpgradeTerminals{}).
				Select("upgrade_terminals.*").
				Joins("JOIN terminal ON terminal.robot_id = upgrade_terminals.robot_id AND terminal.terminal_type = upgrade_terminals.terminal_type").
				Where(
					db.Where("schedule_id IN ?", scheduleIDs).Or("schedule_id IN ?", testScheduleIDs),
				).
				Where("upgrade_terminals.upgrade_status = 0").
				Where("terminal.latest_active_time > ?", time.Now().Unix()-60).
				Order("upgrade_terminals.updated_at ASC").
				Limit(int(MaxUpgradeNum - count)).
				Find(&upgradeTerminals)
			if len(upgradeTerminals) > 0 {
				for _, upgradeTerminal := range upgradeTerminals {
					// 指定的目标版本
					dstVersion := upgradeTerminal.UpgradeVersion
					// 获取终端详情
					terminalDetail, _ := dao.Terminal{}.Detail(context.Background(), dal.Use(mysql.NewDB()), upgradeTerminal.RobotID, upgradeTerminal.TerminalType)
					if terminalDetail == nil {
						terminal.UpdateUpgradeStatus(upgradeTerminal.RobotID, upgradeTerminal.TerminalType, 6) // 更新升级状态，升级失败
						logx.Error(context.Background(), "TerminalUpgrade,不存在的终端")
						continue
					}
					// 1. 先根据架构类型，查找对应软件包
					_, pkgRecords, _ := dao.UpgradePackage{}.List(
						context.Background(),
						dal.Use(mysql.NewDB()),
						upgradeTerminal.UpgradeVersion,
						upgradeTerminal.TerminalType,
						terminalDetail.Arch,
						"", "",
						1, 1,
					)
					// 查询相应版本的文件信息
					var upgradePackage models.UpgradePackage
					if len(pkgRecords) == 1 {
						upgradePackage = *pkgRecords[0]
					} else {
						// 2. 如果第一步没找到，则通过后缀名查找
						// 如果约定了架构定义,就匹配对应架构的版本
						// 去掉给定版本的后缀，加上对应架构后缀
						// 如果对应架构的文件存在就使用对应架构的，否则使用给定版本的(兼容实际版本没有架构带后缀的)
						if define.ArchDef[upgradeTerminal.TerminalType] != nil {
							for _, arch := range define.ArchDef[upgradeTerminal.TerminalType] {
								upgradeTerminal.UpgradeVersion = strings.TrimSuffix(upgradeTerminal.UpgradeVersion, "_"+arch)
							}
							upgradeTerminal.UpgradeVersion = upgradeTerminal.UpgradeVersion + "_" + define.ArchDef[upgradeTerminal.TerminalType][terminalDetail.Arch]
							var count int64
							db.Model(&models.UpgradePackage{}).
								Where("terminal_type = ?", upgradeTerminal.TerminalType).
								Where("version = ?", upgradeTerminal.UpgradeVersion).
								Count(&count)
							// 如果对应的架构版本存在，就使用架构版本
							if count > 0 {
								dstVersion = upgradeTerminal.UpgradeVersion
								// 需要更新升级终端的目标版本
								dao.UpgradeTerminals{}.Update(
									context.Background(),
									dal.Use(mysql.NewDB()),
									upgradeTerminal.ScheduleID, upgradeTerminal.RobotID, upgradeTerminal.TerminalType,
									upgradeTerminal.UpgradeVersion,
								)
							}
						}
						db.Model(&models.UpgradePackage{}).
							Where("terminal_type = ?", upgradeTerminal.TerminalType).
							Where("version = ?", dstVersion).
							Take(&upgradePackage)
					}

					// 获取固件信息
					fi, err := os.Stat(upgradePackage.FilePath)
					if err != nil {
						terminal.UpdateUpgradeStatus(upgradeTerminal.RobotID, upgradeTerminal.TerminalType, 6) // 更新升级状态，升级失败
						logx.Error(context.Background(), "TerminalUpgrade", logx.Err(err))
						continue
					}
					terminal.UpdateUpgradeStatus(upgradeTerminal.RobotID, upgradeTerminal.TerminalType, 1) // 更新升级状态，等待下载
					// 获取更新计划的安装方式-自动或手动
					var sch models.UpgradeSchedule
					db.Model(&models.UpgradeSchedule{}).Where("id = ?", upgradeTerminal.ScheduleID).Take(&sch)
					// 升级通知
					// 判定是否存在对应版本的架构固件
					if upgradeTerminal.TerminalType == "95" {
						wm := &robotc.WildMsg{}
						wm.FromType = robotc.WildMsg_eMobileServiceClient
						wm.FromID = "server"
						wm.ToType = robotc.WildMsg_eMonitorClient
						wm.ToID = "95"
						reqStartUpgrade := &robotc.ReqStartUpgrade{}
						if upgradeTerminal.ForceUpgrade > 0 {
							reqStartUpgrade.Force = true
						}
						reqStartUpgrade.FileName = []byte(upgradePackage.FilePath)
						reqStartUpgrade.FileSize = fi.Size()
						reqStartUpgrade.VersionInfo = []byte(upgradePackage.Version)
						reqStartUpgrade.Md5 = []byte(helper.Md5File(upgradePackage.FilePath))
						if sch.AutoUpgrade == 1 { // 自动安装or手动安装
							reqStartUpgrade.AutoUpgrade = true
						} else {
							reqStartUpgrade.AutoUpgrade = false
						}
						// msg名称
						wm.BodyType = string(reqStartUpgrade.ProtoReflect().Descriptor().FullName())
						// msg内容序列化
						wm.BodyContent, _ = proto.Marshal(reqStartUpgrade)
						// 必须加上前缀，否则不会发布下发的结果
						wm.StrGUID = helper.RandString(16)
						// protobuf序列化
						bytes, _ := proto.Marshal(wm)
						pubsub.CMDPublish(upgradeTerminal.RobotID, upgradeTerminal.TerminalType, "wildmsg", base64.StdEncoding.EncodeToString(bytes))
					} else {
						sendMsg := &devicec.Msg{}
						sendMsg.Ack = 0
						sendMsg.Seq = helper.RandString(16)
						// 消息内容
						reqStartUpgrade := &robotc.ReqStartUpgrade{}
						if upgradeTerminal.ForceUpgrade > 0 {
							reqStartUpgrade.Force = true
						}
						reqStartUpgrade.FileName = []byte(upgradePackage.FilePath)
						reqStartUpgrade.FileSize = fi.Size()
						reqStartUpgrade.VersionInfo = []byte(upgradePackage.Version)
						reqStartUpgrade.Md5 = []byte(helper.Md5File(upgradePackage.FilePath))
						if sch.AutoUpgrade == 1 { // 自动安装or手动安装
							reqStartUpgrade.AutoUpgrade = true
						} else {
							reqStartUpgrade.AutoUpgrade = false
						}
						sendMsg.Name = string(reqStartUpgrade.ProtoReflect().Descriptor().FullName())
						sendMsg.Content, _ = proto.Marshal(reqStartUpgrade)
						// protobuf序列化
						bytes, _ := proto.Marshal(sendMsg)
						pubsub.CMDPublish(upgradeTerminal.RobotID, upgradeTerminal.TerminalType, "msg", base64.StdEncoding.EncodeToString(bytes))
					}
				}
			}
		}
	}
}
